/*
 * Copyright (C) 2012 Tomas Shestakov. <http://code.google.com/p/jmmo/>
 */

package jmmo.engine.property

/**
 * Standard implementation of [[jmmo.engine.property.Property]].
 * For instances creation it is recommended to use `Property` or `PropertyAttr` object.
 * @param init initial value of `Property`
 * @param id unique identifier of `Property`
 * @tparam A type of `Property` value
 * @author Tomas Shestakov
 */
class RegularProperty[A](init: A = jmmo.util.sysDefault, val id: Identifier = Identifier.empty) extends PropertySupport[A] {
  value = init

  def isWritable = true

  def isReadable = true

  override def apply() = value

  override def update(value: A) {
    if (value != this.value) {
      if (needFire(value)) {
        val oldValue = this.value
        this.value = value
        fireChangeEvent(ChangePropertyEvent(this, oldValue, value))
      }
      else {
        this.value = value
      }
    }
  }

  def alter: PartialFunction[Alteration, Unit] = {
      case ChangeAlteration(value: A) => update(value)
  }

  /**
   * Contains the `Property` value
   */
  protected var value: A = _

  /**
   * Define if needed notify listeners about the `Property` value change.
   * @param value new `Property` value
   * @return true if listeners must be notified
   */
  protected def needFire(value: A): Boolean = true

  def canEqual(other: Any) = other.isInstanceOf[Property[A]]

  override def equals(other: Any) = other match {
    case that: Property[A] => (that canEqual this) && id == that.id && this() == that()
    case _ => false
  }

  override def hashCode =
    41 * (
      41 + id.##
    ) + apply().##

  override def toString = "Property(" + this() + "," + id + ")"
}

/**
 * Contains factory methods to create instances of [[jmmo.engine.property.RegularProperty]].
 * Extractor method `unapply` extract only Property `id` and `value` without `attributes`.
 */
object RegularProperty {

  /**
   * Creates a `RegularProperty` with specified initial value, id and attributes.
   * @param init initial value of `Property`
   * @param id unique identifier of `Property`
   * @tparam A type of `Property` value
   */
  def apply[A](init: A = jmmo.util.sysDefault, id: Identifier = Identifier.empty) = new RegularProperty[A](init, id)

  /**
   * This extractor method is called in a pattern match { case RegularProperty(...) => }.
   * @param property a property to extract from
   * @tparam A type of `Property` value
   * @return Property `id` and `value` wrapped in an Option.
   */
  def unapply[A](property: Property[A]): Option[(A, Identifier)] =
    if (property eq null) None
    else Some(property(), property.id)
}
